/**
 * 1. 框架搭建
 * 2. resolve和reject框架搭建
 *    - 修改对象状态
 *    - 设置对象结果值
 *  3. throw 抛出错误
 *  4. 状态只改变一次
 *  5. then方法实现
 *  6. 多个回调实现
 *  7. 同步then 返回结果
 *  8. 异步then 返回结果
 *  9. 异常穿透
 */
class Promise {
	constructor(executor) {
		this.PromiseState = 'pending';
		this.PromiseResult = null;
		this.callbacks = [];
		this.finallyAction = null;
		let resolve = (data) => {
			if (this.PromiseState !== 'pending') return;
			this.PromiseState = 'fulfilled';
			this.PromiseResult = data;
			this.callbacks.forEach(callback => {
				callback.onResolved(data);
			})
			if (this.finallyAction) {
				this.finallyAction();
			}
		};
		let reject = (data) => {
			if (this.PromiseState !== 'pending') return;
			this.PromiseState = 'rejected';
			this.PromiseResult = data;
			this.callbacks.forEach(callback => {
				callback.onRejected(data);
			})
			if (this.finallyAction) {
				this.finallyAction();
			}
		};
		try {
			executor(resolve, reject);
		} catch (e) {
			reject(e);
		}

	}

	then(onResolved, onRejected) {
		if (typeof onRejected !== 'function') {
			onRejected = reason => {
				throw reason
			};
		}
		if (typeof onResolved !== 'function') {
			onResolved = value => {
				throw  value
			};
		}
		return new Promise((resolve, reject) => {
			let callback = (func) => {
				try {
					let result = func(this.PromiseResult);
					if (result instanceof Promise) {
						result.then(v => {
							resolve(v);
						}, r => {
							reject(r);
						})
					} else {
						resolve(result);
					}
				} catch (e) {
					reject(e);
				}
			}
			if (this.PromiseState === 'fulfilled') {
				setTimeout(() => {
					callback(onResolved)
				});
			}
			if (this.PromiseState === 'rejected') {
				setTimeout(() => {
					callback(onRejected)
				});
			}
			if (this.PromiseState === 'pending') {
				this.callbacks.push({
					onResolved: () => {
						setTimeout(() => {
							callback(onResolved);
						});
					},
					onRejected: () => {
						setTimeout(() => {
							callback(onRejected)
						});
					}
				})
			}
		});
	}

	catch(onRejected) {
		return this.then(undefined, onRejected)
	}

	finally(onFinally) {
		this.finallyAction = onFinally;
	}

	static resolve(value) {
		return new Promise((resolve, reject) => {
			if (value instanceof Promise) {
				value.then(v => {
					resolve(v);
				}, r => {
					reject(r)
				})
			} else {
				resolve(value);
			}
		})
	}

	static reject(reason) {
		return new Promise((resolve, reject) => {
			reject(reason);
		})
	}

	static all(promise) {
		return new Promise((resolve, reject) => {
			let count = 0, result = [];
			for (let i = 0; i < promise.length; i++) {
				promise[i].then(v => {
					count++;
					result[i] = v;
					if (count === promise.length) {
						resolve(result);
					}
				}, r => {
					reject(r);
				})
			}
		})
	}

	static race(promises) {
		return new Promise((resolve, reject) => {
			for (let i = 0; i < promises.length; i++) {
				promises[i].then(v => {
					resolve(v);
				}, r => {
					reject(r);
				})
			}
		})
	}

	static allSettled(promises) {
		return new Promise((resolve) => {
			let result = [],
				count = 0;
			for (let i = 0; i < promises.length; i++) {
				promises[i].then(value => {
					result[i] = {status: 'fulfilled', value};
					count++;
					if (count === promises.length) {
						resolve(result);
					}
				}, reason => {
					result[i] = {status: 'rejected', reason};
					count++;
					if (count === promises.length) {
						resolve(result);
					}
				})
			}
		})
	}

	/**
	 * 只要其中的一个 promise 成功，就返回那个已经成功的 promise 。
	 * 如果可迭代对象中没有一个 promise 成功，
	 * 就返回一个失败的 promise 和AggregateError类型的实例，
	 * 它是 Error 的一个子类，用于把单一的错误集合在一起。本质上，这个方法和Promise.all()是相反的
	 * @param {[Promise]} promises
	 */
	static any(promises) {
		return new Promise((resolve, reject) => {
			let result = [],
				count = 0,
				res = () => {
					let success = result.find(item => item.status === 'fulfilled')
					if (success) {
						resolve(success.value);
					} else {
						let errors = [];
						for (let j = 0; j < result.length; j++) {
							errors.push(result[j].reason)
						}
						let r = new AggregateError(errors, '所有的promise都失败了')
						reject(r);
					}
				}
			for (let i = 0; i < promises.length; i++) {
				promises[i].then(value => {
					result.push({status: "fulfilled", value});
					count++;
					if (count === promises.length) {// n个都执行完了,返回成功的一个
						res();
					}
				}, reason => {
					result.push({status: 'rejected', reason});
					count++;
					if (count === promises.length) {// n个都执行完了,没有成功的,返回失败状态
						res();
					}
				})
			}
		})
	}
}
